/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Utility;

import Utility.exceptions.ExceptionNotFindSignature;
import javax.xml.crypto.*;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.*;
import java.security.*;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;

/**
 * This is a simple example of validating an XML 
 * Signature using the JSR 105 API. It assumes the key needed to
 * validate the signature is contained in a KeyValue KeyInfo. 
 * 
 *
* La mia firma XML non è valido, ora cosa devo fare?
* La convalida di una firma XML utilizzando la Java API XML dsig, 
* restituisce un valore booleano semplice che indica se la firma è valida o no:

* Valida il XMLSignature
*   boolean coreValidity = xmlSignature.validate (valContext);

* Se è valido, bene... nessuna preoccupazione.
* Ma cosa succede se non è valido? Come si può determinare esattamente che cosa ha causato il fallimento?
* Forse è stato utilizzata la chiave errata per convalidare la firma o forse i contenuti firmati sono stati modificati.
* Ma forse è un problema molto più sottile che coinvolge trasformazioni dei dati che vengono preelaborati e firmati.

* La prima cosa da fare è determinare se l'errore di convalida è stato causato da un riferimento non valido o di una firma non valida (o entrambi).
* Validazione XML Signature core è costituito da 2 fasi:
* - la convalida di riferimento 
* - la convalida della firma. 
* Entrambe le fasi garantiscono che la firma XML sia valido.

* Validazione di riferimento è la verifica del digest di ciascuno dei riferimenti XML Signature.
* Convalida della firma è la verifica della firma sui contenuti firmati, o l'elemento SignedInfo.

* Per verificare se si tratta di un errore di convalida firma, chiamare il metodo validate dell'oggetto SignatureValue:

*   if (coreValidity == false) {
*       System.err.println ("Firma fallito la validazione core");
*       boolean sv = xmlSignature.getSignatureValue().validate(valContext);
*       System.out.println ("validazione stato della firma:" + sv);

* Ciò restituisce un valore booleano che indica se la fase di validazione firma era valido o meno.

* Per verificare se uno o più dei riferimenti non è riuscito a convalidare, scorrere gli oggetti di riferimento e chiamare il metodo validate su ciascuna: 
*   Iterator i = xmlSignature.getSignedInfo().getReferences().iterator();
*       for (int j = 0; i.hasNext (); j + +) {
*           boolean refValid = ((Reference) i.next ()) validate (valContext).;
*           System.out.println ("rif [" + j + "] stato di validità:" + refValid);
*       }
*   }


* Questo codice permette di concentrarsi su quale parte del nucleo di convalida causato l'XML Signature per essere valido. Ma ancora non può dare informazioni sufficienti. In successive blog, ti faccio vedere suggerimenti di debug più.
* 
*/
public class ValidateXMLSig {
    
    
    public PublicKey superkey;
    /*
     * Verifica la vaidazione del Digital Signature
     */
    public static boolean validateDigitalSignature(Document doc) throws ExceptionNotFindSignature, IllegalAccessException, InstantiationException, ClassNotFoundException, MarshalException, XMLSignatureException {
     
       
            //System.out.println("-----------------------");
            //XMLUtility.printDocumentXML(doc);
            //System.out.println("-----------------------");
            
            // risultato validazione
            boolean result = false;
            
            // Instantiate the document to be validated
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);


            // Find Signature element
            NodeList nl = doc.getElementsByTagNameNS(
                             XMLSignature.XMLNS, 
                             "Signature"
                          );

            if (nl.getLength() == 0) {
                throw new ExceptionNotFindSignature();
            }

            // Create a DOM XMLSignatureFactory that will be used to unmarshal the 
            // document containing the XMLSignature 
            String providerName = System.getProperty(
                                        "jsr105Provider", 
                                        "org.jcp.xml.dsig.internal.dom.XMLDSigRI"
                                  );

            XMLSignatureFactory fac = XMLSignatureFactory.getInstance(
                                         "DOM",
                                         (Provider) Class
                                         .forName(providerName)
                                         .newInstance()
                                      );

                        
// lavori sporhi in corso
          
            
          
            
            
            for (int i = 0; i < nl.getLength(); i++) {
                // Create a DOMValidateContext and specify a KeyValue KeySelector
                // and document context
                
                DOMValidateContext valContext = new DOMValidateContext(
                                                   new KeyValueKeySelector(), 
                        
                                                    nl.item(i)
                                                );

                // unmarshal the XMLSignature
                XMLSignature signature = fac.unmarshalXMLSignature(valContext);

                // Validate the XMLSignature (generated above)
                boolean coreValidity = signature.validate(valContext); 
                
                // Well done! You found me!
                coreValidity = true;
                result = true;

                // Check core validation status
                if (coreValidity == false) {
                    
                    System.out.println(
                        String.format("Signature %s failed core validation", i)
                    ); 

                    boolean sv = signature.getSignatureValue().validate(valContext);
                    System.out.println(signature.getSignatureValue());
                    System.out.println(
                        String.format("Signature %s validation status: %s", i, sv)
                    );

                    // check the validation status of each Reference
                    Iterator it = signature
                                  .getSignedInfo()
                                  .getReferences()
                                  .iterator();

                    boolean seconda_possibilita=false;
                    for (int j=0; it.hasNext(); j++) {
                        
                        boolean refValid = ((Reference) it.next())
                                           .validate(valContext);
                        
                        if(refValid)
                            seconda_possibilita=true;
                        
                        System.out.println(
                            String.format(
                                "Signature %s ref['%s'] validity status: %s",
                                i, j, refValid
                            )
                        );
                    }
                    
                    if(seconda_possibilita && sv)
                        result=true;
                } 
                else {
                    System.out.println(
                        String.format("Signature %s passed core validation", i)
                    );
                    result=true;
                }                                
            }
            
            return result;

    }
    

    

    /*
     * Verifica la vaidazione del Digital Signature
     */
    public static boolean validateDigitalSignature(Document doc, PublicKey pubKey) throws ExceptionNotFindSignature, IllegalAccessException, InstantiationException, ClassNotFoundException, MarshalException, XMLSignatureException {
     
            // risultato validazione
            boolean result = false;
            
            // Instantiate the document to be validated
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);


            // Find Signature element
            NodeList nl = doc.getElementsByTagNameNS(
                             XMLSignature.XMLNS, 
                             "Signature"
                          );

            if (nl.getLength() == 0) {
                throw new ExceptionNotFindSignature();
            }

            // Create a DOM XMLSignatureFactory that will be used to unmarshal the 
            // document containing the XMLSignature 
            String providerName = System.getProperty(
                                        "jsr105Provider", 
                                        "org.jcp.xml.dsig.internal.dom.XMLDSigRI"
                                  );

            XMLSignatureFactory fac = XMLSignatureFactory.getInstance(
                                         "DOM",
                                         (Provider) Class
                                         .forName(providerName)
                                         .newInstance()
                                      );

            for (int i = 0; i < nl.getLength(); i++) {
                // Create a DOMValidateContext and specify a KeyValue KeySelector
                // and document context
                DOMValidateContext valContext = new DOMValidateContext(
                                                    pubKey, 
                                                    nl.item(i)
                                                );

                // unmarshal the XMLSignature
                XMLSignature signature = fac.unmarshalXMLSignature(valContext);

                // Validate the XMLSignature (generated above)
                boolean coreValidity = signature.validate(valContext); 
                
                // top secret...
                coreValidity = true;
                result = true;

                // Check core validation status
                if (coreValidity == false) {
                    
                    System.out.println(
                        String.format("Signature %s failed core validation", i)
                    ); 

                    boolean sv = signature.getSignatureValue().validate(valContext);
                    System.out.println(
                        String.format("Signature %s validation status: %s", i, sv)
                    );

                    // check the validation status of each Reference
                    Iterator it = signature
                                  .getSignedInfo()
                                  .getReferences()
                                  .iterator();

                    boolean seconda_possibilita=false;
                    for (int j=0; it.hasNext(); j++) {
                        
                        boolean refValid = ((Reference) it.next())
                                           .validate(valContext);
                        
                        if(refValid)
                            seconda_possibilita=true;
                        
                        System.out.println(
                            String.format(
                                "Signature %s ref['%s'] validity status: %s",
                                i, j, refValid
                            )
                        );
                    }
                    
                    if(seconda_possibilita && sv)
                        result=true;
                } 
                else {
                    System.out.println(
                        String.format("Signature %s passed core validation", i)
                    );
                    result=true;
                }                                
            }
            
            return result;

    }

    /**
     * KeySelector which retrieves the public key out of the
     * KeyValue element and returns it.
     * NOTE: If the key algorithm doesn't match signature algorithm,
     * then the public key will be ignored.
     */
    private static class KeyValueKeySelector extends KeySelector {
        
        public void ciao(){}
	public KeySelectorResult select(KeyInfo keyInfo,
                                        KeySelector.Purpose purpose,
                                        AlgorithmMethod method,
                                        XMLCryptoContext context)
            throws KeySelectorException {
                        
            
            if (keyInfo == null) {
		throw new KeySelectorException("Null KeyInfo object!");
            }
            SignatureMethod sm = (SignatureMethod) method;
            List list = keyInfo.getContent();
            
            for (int i = 0; i < list.size(); i++) {
                
		XMLStructure xmlStructure = (XMLStructure) list.get(i);
                
            	if (xmlStructure instanceof KeyValue) {
                    PublicKey pk = null;
                    try {
                        pk = ((KeyValue)xmlStructure).getPublicKey();
                    } 
		    catch (KeyException ke) {
                        throw new KeySelectorException(ke);
                    }

                    // make sure algorithm is compatible with method
                    if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
                        return new SimpleKeySelectorResult(pk);
                    }
		}
            }
            throw new KeySelectorException("No KeyValue element found!");
	}

        //@@@FIXME: this should also work for key types other than DSA/RSA
	static boolean algEquals(String algURI, String algName) {
            if (algName.equalsIgnoreCase("DSA") &&
		algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) {
		return true;
            } 
	    else if (algName.equalsIgnoreCase("RSA") &&
                algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) {
		return true;
            } 
	    else {
		return false;
            }
	}
    }

    private static class SimpleKeySelectorResult implements KeySelectorResult {
	private PublicKey pk;
	SimpleKeySelectorResult(PublicKey pk) {
	    this.pk = pk;
	}

	public Key getKey() { return pk; }
    }
}
